/***
 * Copyright (c) 2021-2031 murenchao
 * fig is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *       http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */
package cool.taomu.software.fig.asm.generate

import java.io.PrintWriter
import java.lang.invoke.CallSite
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
import java.util.Vector
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Handle
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type
import org.objectweb.asm.commons.GeneratorAdapter
import org.objectweb.asm.commons.Method
import org.objectweb.asm.util.TraceClassVisitor

class GenerateUtils {
    /**
     * 定义一个接口用来提供除构造函数外的字节码操作
     */
    static interface Asm {
        def void code(ClassVisitor visitor, Class<?> superClass, Class<?>[] interfaces);
    }

    def static invokeDynamic(GeneratorAdapter ga, Class<?> zlass, Method method, Class<?>[] params) {
        var mt = MethodType.methodType(CallSite, MethodHandles.Lookup, String, MethodType);
        var bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(zlass), "bootstrap",
            mt.toMethodDescriptorString(), false);
        // var m = Method.getMethod(method);
        ga.invokeDynamic(method.name, method.descriptor, bootstrap);
    }

    def static invokeDynamic(GeneratorAdapter ga, Class<?> zlass, Method method) {
        var mt = MethodType.methodType(CallSite, MethodHandles.Lookup, String, MethodType);
        var bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(zlass), "bootstrap",
            mt.toMethodDescriptorString(), false);
        ga.invokeDynamic(method.name, method.descriptor, bootstrap);
    }

    def gen(String className, Class<?> superClass, Class<?>[] interfaces, Asm asm) {
        gen(className, superClass, interfaces, asm, false);
    }

    /**
     * className 要生成的类名
     * superClass 要继承的父类
     * interfaces 要继承的接口
     * Asm 字节码操作
     */
    def gen(String className, Class<?> superClass, Class<?>[] interfaces, Asm asm, boolean isDebug) {
        /**
         * 创建一个ClassWriter的对象  
         * COMPUTE_MAXS 告诉ASM 自动计算栈的最大值以及最大数量的方法的本地变量。
         * COMPUTE_FRAMES 标识让ASM 自动计算方法的栈桢
         */
        val classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
        var PrintWriter pw = null;
        if (isDebug) {
            pw = new PrintWriter(System.err)
        }
        /**
         * 创建一个TraceClassVisitor对象，将java字节码以文本的方式展现出来，方便查看生成的字节码
         */
        val ClassVisitor classVisitor = new TraceClassVisitor(classWriter, pw);
        /**
         * 将 class[] 类的数据转换为 String[] 
         */
        val interfaceStr = new Vector<String>();
        if (interfaces !== null) {
            interfaces.filterNull.forEach [ inter |
                interfaceStr.add(inter.name.replace(".", "/"));
            ]
        }
        /**
         * 判断如果superClass 为 null 就设置一个默认值 Object 就是
         * 目的是创建的子类如果没有父类 就默认继承Object类
         */
        val superZlass = superClass === null ? Object : superClass;
        // 开始创建一个类，创建的类版本为 jdk1.8,类的作用域为public
        classVisitor.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, className, null, superZlass.typeName.replace(".", "/"),
            interfaceStr);
        /**
         * 对继承的父类中的构建函数进行处理
         * 就是创建子类构造函数，并调用父类构造
         */
        superZlass.constructors.filterNull.forEach [ c |
            var m = Method.getMethod(c);
            val ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC, m, null, null, classVisitor);
            ga.loadThis();
            if (c.parameters.size > 0) {
                ga.loadArgs(0, c.parameters.size - 1);
            }
            ga.invokeConstructor(Type.getType(superZlass), m)
            ga.returnValue()
            ga.endMethod();
        ]
        if (asm !== null) {
            asm.code(classVisitor, superZlass, interfaces);
        }
        classVisitor.visitEnd()
        // 返回创建的字节码
        return classWriter.toByteArray()
    }
}
